{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在上一章节，我们使用传统机器学习算法来解决了文本分类问题，从本章开始我们将尝试使用深度学习方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## **Task4 基于深度学习的文本分类1-fastText**\n",
    "\n",
    "与传统机器学习不同，深度学习既提供特征提取功能，也可以完成分类的功能。从本章开始我们将学习如何使用深度学习来完成文本表示。\n",
    "\n",
    "### **学习目标**\n",
    "\n",
    "- 学习FastText的使用和基础原理\n",
    "- 学会使用验证集进行调参\n",
    "\n",
    "### **文本表示方法 Part2**\n",
    "\n",
    "#### **现有文本表示方法的缺陷**\n",
    "\n",
    "在上一章节，我们介绍几种文本表示方法：\n",
    "\n",
    "- One-hot\n",
    "- Bag of Words\n",
    "- N-gram\n",
    "- TF-IDF\n",
    "\n",
    "也通过sklean进行了相应的实践，相信你也有了初步的认知。但上述方法都或多或少存在一定的问题：转换得到的向量维度很高，需要较长的训练实践；没有考虑单词与单词之间的关系，只是进行了统计。\n",
    "\n",
    "与这些表示方法不同，深度学习也可以用于文本表示，还可以将其映射到一个低纬空间。其中比较典型的例子有：FastText、Word2Vec和Bert。在本章我们将介绍FastText，将在后面的内容介绍Word2Vec和Bert。\n",
    "\n",
    "#### **FastText**\n",
    "\n",
    "FastText是一种典型的深度学习词向量的表示方法，它非常简单通过Embedding层将单词映射到稠密空间，然后将句子中所有的单词在Embedding空间中进行平均，进而完成分类操作。\n",
    "\n",
    "所以FastText是一个三层的神经网络，输入层、隐含层和输出层。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "<div align=center><img src=\"http://jupter-oss.oss-cn-hangzhou.aliyuncs.com/public/files/image/1095279501877/1594909720411_wruLzMgC7N.jpg\" width=\"50%\" height=\"50%\"></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下图是使用keras实现的FastText网络结构："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "<div align=center><img src=\"http://jupter-oss.oss-cn-hangzhou.aliyuncs.com/public/files/image/1095279501877/1594909821168_NvB7c98dSc.jpg\" width=\"500\" height=\"500\"></div>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "FastText在文本分类任务上，是优于TF-IDF的：\n",
    "\n",
    "- FastText用单词的Embedding叠加获得的文档向量，将相似的句子分为一类\n",
    "- FastText学习到的Embedding空间维度比较低，可以快速进行训练\n",
    "\n",
    "如果想深度学习，可以参考论文：\n",
    "\n",
    "Bag of Tricks for Efficient Text Classification, https://arxiv.org/abs/1607.01759\n",
    "\n",
    "### **基于FastText的文本分类**\n",
    "\n",
    "FastText可以快速的在CPU上进行训练，最好的实践方法就是官方开源的版本：\n",
    "https://github.com/facebookresearch/fastText/tree/master/python\n",
    "\n",
    "- pip安装\n",
    "\n",
    "```\n",
    "pip install fasttext\n",
    "```\n",
    "\n",
    "- 源码安装\n",
    "\n",
    "```\n",
    "git clone https://github.com/facebookresearch/fastText.git\n",
    "cd fastText\n",
    "sudo pip install .\n",
    "```\n",
    "\n",
    "两种安装方法都可以安装，如果你是初学者可以优先考虑使用pip安装。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "from sklearn.metrics import f1_score\n",
    "\n",
    "# 转换为FastText需要的格式\n",
    "train_df = pd.read_csv('../data/train_set.csv', sep='\\t', nrows=15000)\n",
    "train_df['label_ft'] = '__label__' + train_df['label'].astype(str)\n",
    "train_df[['text','label_ft']].iloc[:-5000].to_csv('train.csv', index=None, header=None, sep='\\t')\n",
    "\n",
    "import fasttext\n",
    "model = fasttext.train_supervised('train.csv', lr=1.0, wordNgrams=2, \n",
    "                                  verbose=2, minCount=1, epoch=25, loss=\"hs\")\n",
    "\n",
    "val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[-5000:]['text']]\n",
    "print(f1_score(train_df['label'].values[-5000:].astype(str), val_pred, average='macro'))\n",
    "# 0.82"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "markdown",
   "source": [
    "此时数据量比较小得分为0.82，当不断增加训练集数量时，FastText的精度也会不断增加5w条训练样本时，验证集得分可以到0.89-0.90左右。\n",
    "\n",
    "### **如何使用验证集调参**\n",
    "\n",
    "在使用TF-IDF和FastText中，有一些模型的参数需要选择，这些参数会在一定程度上影响模型的精度，那么如何选择这些参数呢？\n",
    "\n",
    "- 通过阅读文档，要弄清楚这些参数的大致含义，那些参数会增加模型的复杂度\n",
    "- 通过在验证集上进行验证模型精度，找到模型在是否过拟合还是欠拟合"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "ename": "ModuleNotFoundError",
     "evalue": "No module named 'fasttext'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-2-1b7944b164c6>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      7\u001b[0m \u001b[0mtrain_df\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'text'\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;34m'label_ft'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m5000\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mto_csv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'train.csv'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mheader\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msep\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'\\t'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      8\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 9\u001b[1;33m \u001b[1;32mimport\u001b[0m \u001b[0mfasttext\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     10\u001b[0m model = fasttext.train_supervised('train.csv', lr=1.0, wordNgrams=2, \n\u001b[0;32m     11\u001b[0m                                   verbose=2, minCount=1, epoch=25, loss=\"hs\")\n",
      "\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'fasttext'"
     ]
    }
   ],
   "source": [
    "\n",
    "\n",
    "<div align=center><img src=\"http://jupter-oss.oss-cn-hangzhou.aliyuncs.com/public/files/image/1095279501877/1594909879453_RrvunJz6cT.jpg\" width=\"50%\" height=\"50%\"></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此时数据量比较小得分为0.82，当不断增加训练集数量时，FastText的精度也会不断增加5w条训练样本时，验证集得分可以到0.89-0.90左右。\n",
    "\n",
    "### **如何使用验证集调参**\n",
    "\n",
    "在使用TF-IDF和FastText中，有一些模型的参数需要选择，这些参数会在一定程度上影响模型的精度，那么如何选择这些参数呢？\n",
    "\n",
    "- 通过阅读文档，要弄清楚这些参数的大致含义，那些参数会增加模型的复杂度\n",
    "- 通过在验证集上进行验证模型精度，找到模型在是否过拟合还是欠拟合"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "<div align=center><img src=\"http://jupter-oss.oss-cn-hangzhou.aliyuncs.com/public/files/image/1095279501877/1594909879453_RrvunJz6cT.jpg\" width=\"50%\" height=\"50%\"></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里我们使用10折交叉验证，每折使用9/10的数据进行训练，剩余1/10作为验证集检验模型的效果。这里需要注意每折的划分必须保证标签的分布与整个数据集的分布一致。\n",
    "\n",
    "```\n",
    "label2id = {}\n",
    "for i in range(total):\n",
    "    label = str(all_labels[i])\n",
    "    if label not in label2id:\n",
    "        label2id[label] = [i]\n",
    "    else:\n",
    "        label2id[label].append(i)\n",
    "```\n",
    "\n",
    "通过10折划分，我们一共得到了10份分布一致的数据，索引分别为0到9，每次通过将一份数据作为验证集，剩余数据作为训练集，获得了所有数据的10种分割。不失一般性，我们选择最后一份完成剩余的实验，即索引为9的一份做为验证集，索引为1-8的作为训练集，然后基于验证集的结果调整超参数，使得模型性能更优。\n",
    "\n",
    "### **本章小结**\n",
    "\n",
    "本章介绍了FastText的原理和基础使用，并进行相应的实践。然后介绍了通过10折交叉验证划分数据集。\n",
    "\n",
    "### **本章作业**\n",
    "\n",
    "- 阅读FastText的文档，尝试修改参数，得到更好的分数\n",
    "- 基于验证集的结果调整超参数，使得模型性能更优\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  },
  "pycharm": {
   "stem_cell": {
    "cell_type": "raw",
    "source": [],
    "metadata": {
     "collapsed": false
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}